//
// Created by Administrator on 2022/1/17.
// Torus模型
//

#include "glad/glad.h"
#include "GLFW/glfw3.h"
#include "Common/Utils.h"
#include "Common/Torus.h"

#define numVAOs 1
#define numVBOs 4
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
GLuint texture;
GLuint rendingProgram;
Torus torus(0.5f, 0.2f, 48);

glm::mat4 projMatrix, viewMatrix, torusMatrix;
glm::vec3 cameraPos;

void Init(GLFWwindow* window);
void Update(GLFWwindow* window, double currentTime);
void Render();

void SetupVertices();
void OnWindowSizeCallback(GLFWwindow* window, int w, int h);

int main()
{
    if(!glfwInit())
    {
        exit(EXIT_FAILURE);
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow* window = glfwCreateWindow(800, 600, "Torus", nullptr, nullptr);
    if(!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwMakeContextCurrent(window);
    glfwSetWindowSizeCallback(window, OnWindowSizeCallback);

    if(!gladLoadGLLoader(GLADloadproc(glfwGetProcAddress)))
    {
        glfwDestroyWindow(window);
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    Init(window);
    glfwSwapInterval(1);
    while(!glfwWindowShouldClose(window))
    {
        Update(window, glfwGetTime());
        Render();
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwDestroyWindow(window);
    glfwTerminate();

    exit(EXIT_SUCCESS);
}

void Init(GLFWwindow* window)
{
    rendingProgram = Utils::CreateShaderProgram("../../Chapter6/vertShader.glsl",
                                                "../../Chapter6/fragShader.glsl");
    texture = Utils::LoadTexture("../../Chapter6/wall.jpg");
    SetupVertices();

    cameraPos = glm::vec3(0.0f, 0.0f, 8.0f);
    int w, h;
    glfwGetFramebufferSize(window, &w, &h);
    projMatrix = glm::perspective(Utils::Deg2Rad(60.0f), float(w) / float(h), 0.1f, 1000.0f);

    // 线框图
    // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
void Update(GLFWwindow* window, double currentTime)
{
    viewMatrix = glm::translate(glm::mat4(1.0f), -cameraPos);
    torusMatrix = glm::rotate(glm::mat4(1.0f), float(currentTime), glm::vec3(0.0f, 1.0f, 0.0f));
    torusMatrix = glm::rotate(torusMatrix, Utils::Deg2Rad(30.0f), glm::vec3(1.0f, 0.0f, 0.0f));
    torusMatrix = glm::scale(torusMatrix, glm::vec3(3.0f));
}
void Render()
{
    glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(rendingProgram);
    GLint vpMatrixLoc = glGetUniformLocation(rendingProgram, "vpMatrix");
    GLint objectToWorldMatrixLoc = glGetUniformLocation(rendingProgram, "objectToWorldMatrix");

    glUniformMatrix4fv(vpMatrixLoc, 1, GL_FALSE, glm::value_ptr(projMatrix * viewMatrix));
    glUniformMatrix4fv(objectToWorldMatrixLoc, 1, GL_FALSE, glm::value_ptr(torusMatrix));

    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);

    glDrawElements(GL_TRIANGLES, torus.getNumIndices(), GL_UNSIGNED_INT, nullptr);
}

void SetupVertices()
{
    int numVertices = torus.getNumVertices();
    int numIndices = torus.getNumIndices();

    glGenVertexArrays(numVAOs, vao);
    glBindVertexArray(vao[0]);
    glGenBuffers(numVBOs, vbo);

    glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, numVertices * 3 * sizeof(float), torus.getVertices().data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
    glBufferData(GL_ARRAY_BUFFER, numVertices * 2 * sizeof(float), torus.getUVs().data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
    glBufferData(GL_ARRAY_BUFFER, numVertices * 3 * sizeof(float), torus.getNormals().data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[3]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(int), torus.getIndices().data(), GL_STATIC_DRAW);
}
void OnWindowSizeCallback(GLFWwindow* window, int w, int h)
{
    glViewport(0, 0, w, h);
    projMatrix = glm::perspective(Utils::Deg2Rad(60.0f), float(w) / float(h), 0.1f, 1000.0f);
}
